package cn.snmp.mutual.uk.stack;

import cn.snmp.mutual.uk.beans.UsmDiscoveryBean;
import java.util.*;
import java.io.*;
import cn.snmp.mutual.uk.event.RequestPduListener;
import cn.snmp.mutual.uk.pdu.DiscoveryPdu;
import cn.snmp.mutual.uk.util.SnmpUtilities;

public abstract class SnmpContextv3Basis extends AbstractSnmpContext implements SnmpContextv3Face, Cloneable {
    private static final String version_id = "@(#)$Id: SnmpContextv3Basis.java,v 3.17 2009/03/05 15:51:42 birgita Exp $ Copyright Westhawk Ltd";
    protected String userName;
    protected boolean useAuthentication;
    protected String userAuthenticationPassword;
    protected byte[] userAuthKeyMD5;
    protected byte[] userAuthKeySHA1;
    protected int authenticationProtocol;
    protected int privacyProtocol;
    protected boolean usePrivacy;
    protected String userPrivacyPassword;
    protected byte[] userPrivKeyMD5;
    protected byte[] userPrivKeySHA1;
    protected byte[] contextEngineId;
    protected String contextName;
    protected UsmAgent usmAgent;
    private Hashtable msgIdHash;
    private static int next_id;

    public SnmpContextv3Basis(final String host, final int port) throws IOException {
        this(host, port, null, "Standard");
    }

    public SnmpContextv3Basis(final String host, final int port, final String typeSocketA) throws IOException {
        this(host, port, null, typeSocketA);
    }

    public SnmpContextv3Basis(final String host, final int port, final String bindAddress, final String typeSocketA) throws IOException {
        super(host, port, bindAddress, typeSocketA);
        this.userName = "initial";
        this.useAuthentication = false;
        this.userAuthKeyMD5 = null;
        this.userAuthKeySHA1 = null;
        this.authenticationProtocol = 0;
        this.privacyProtocol = 2;
        this.usePrivacy = false;
        this.userPrivKeyMD5 = null;
        this.userPrivKeySHA1 = null;
        this.contextEngineId = new byte[0];
        this.contextName = "";
        this.usmAgent = null;
        this.msgIdHash = new Hashtable(20);
        if (TimeWindow.getCurrent() == null) {
            final TimeWindow timew = new TimeWindow();
        }
        this.setUsmAgent(this.createUsmAgent());
    }

    @Override
    public int getVersion() {
        return 3;
    }

    public String getUserName() {
        return this.userName;
    }

    public void setUserName(final String newUserName) {
        this.userName = newUserName;
    }

    public boolean isUseAuthentication() {
        return this.useAuthentication;
    }

    public void setUseAuthentication(final boolean newUseAuthentication) {
        this.useAuthentication = newUseAuthentication;
    }

    public String getUserAuthenticationPassword() {
        return this.userAuthenticationPassword;
    }

    public void setUserAuthenticationPassword(final String newUserAuthPassword) {
        if (newUserAuthPassword != null && !newUserAuthPassword.equals(this.userAuthenticationPassword)) {
            this.userAuthenticationPassword = newUserAuthPassword;
            this.userAuthKeyMD5 = null;
            this.userAuthKeySHA1 = null;
        }
    }

    public void setAuthenticationProtocol(final int protocol) throws IllegalArgumentException {
        if (protocol == 0 || protocol == 1) {
            if (protocol != this.authenticationProtocol) {
                this.authenticationProtocol = protocol;
            }
            return;
        }
        throw new IllegalArgumentException("Authentication Protocol should be MD5 or SHA1");
    }

    public int getAuthenticationProtocol() {
        return this.authenticationProtocol;
    }

    public void setPrivacyProtocol(final int protocol) throws IllegalArgumentException {
        if (protocol == 2 || protocol == 3) {
            if (protocol != this.privacyProtocol) {
                this.privacyProtocol = protocol;
            }
            return;
        }
        throw new IllegalArgumentException("Privacy Encryption should be AES or DES");
    }

    public int getPrivacyProtocol() {
        return this.privacyProtocol;
    }

    byte[] getAuthenticationPasswordKeyMD5() {
        if (this.userAuthKeyMD5 == null) {
            this.userAuthKeyMD5 = SnmpUtilities.passwordToKeyMD5(this.userAuthenticationPassword);
        }
        return this.userAuthKeyMD5;
    }

    byte[] getAuthenticationPasswordKeySHA1() {
        if (this.userAuthKeySHA1 == null) {
            this.userAuthKeySHA1 = SnmpUtilities.passwordToKeySHA1(this.userAuthenticationPassword);
        }
        return this.userAuthKeySHA1;
    }

    byte[] getPrivacyPasswordKeyMD5() {
        if (this.userPrivKeyMD5 == null) {
            this.userPrivKeyMD5 = SnmpUtilities.passwordToKeyMD5(this.userPrivacyPassword);
        }
        return this.userPrivKeyMD5;
    }

    byte[] getPrivacyPasswordKeySHA1() {
        if (this.userPrivKeySHA1 == null) {
            this.userPrivKeySHA1 = SnmpUtilities.passwordToKeySHA1(this.userPrivacyPassword);
        }
        return this.userPrivKeySHA1;
    }

    public boolean isUsePrivacy() {
        return this.usePrivacy;
    }

    public void setUsePrivacy(final boolean newUsePrivacy) {
        this.usePrivacy = newUsePrivacy;
    }

    public String getUserPrivacyPassword() {
        return this.userPrivacyPassword;
    }

    public void setUserPrivacyPassword(final String newUserPrivacyPassword) {
        if (newUserPrivacyPassword != null && !newUserPrivacyPassword.equals(this.userPrivacyPassword)) {
            this.userPrivacyPassword = newUserPrivacyPassword;
            this.userPrivKeyMD5 = null;
            this.userPrivKeySHA1 = null;
        }
    }

    public void setContextEngineId(final byte[] newContextEngineId) throws IllegalArgumentException {
        if (newContextEngineId != null) {
            this.contextEngineId = newContextEngineId;
            return;
        }
        throw new IllegalArgumentException("contextEngineId is null");
    }

    public byte[] getContextEngineId() {
        return this.contextEngineId;
    }

    public void setContextName(final String newContextName) {
        this.contextName = newContextName;
    }

    public String getContextName() {
        return this.contextName;
    }

    public boolean addDiscoveryPdu(final DiscoveryPdu pdu) throws IOException, PduException {
        return this.addPdu(pdu, false);
    }

    @Override
    public boolean addPdu(final Pdu pdu) throws IOException, PduException {
        return this.addPdu(pdu, true);
    }

    protected UsmAgent createUsmAgent() {
        return new DefaultUsmAgent();
    }

    public void setUsmAgent(final UsmAgent agent) {
        this.usmAgent = agent;
    }

    public UsmAgent getUsmAgent() {
        return this.usmAgent;
    }

    protected boolean addPdu(final Pdu pdu, final boolean checkDiscovery) throws IOException, PduException {
        Integer msgId = pdu.snmpv3MsgId;
        if (msgId == null) {
            msgId = new Integer(SnmpContextv3Basis.next_id++);
        } else if (pdu.isExpectingResponse()) {
            msgId = new Integer(SnmpContextv3Basis.next_id++);
        }
        pdu.snmpv3MsgId = msgId;
        this.msgIdHash.put(msgId, new Integer(pdu.req_id));
        if (AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".addPdu(): msgId=" + msgId.toString() + ", Pdu reqId=" + pdu.req_id);
        }
        if (checkDiscovery && !this.isAuthoritative(pdu.getMsgType())) {
            this.discoverIfNeeded(pdu);
        }
        final boolean added = super.addPdu(pdu);
        return added;
    }

    @Override
    public synchronized boolean removePdu(final int rid) {
        final boolean removed = super.removePdu(rid);
        if (removed) {
            final Enumeration keys = this.msgIdHash.keys();
            Integer msgIdI = null;
            boolean found;
            Integer pduIdI;
            for (found = false; keys.hasMoreElements() && !found; found = (pduIdI == rid)) {
                msgIdI = (Integer) keys.nextElement();
                pduIdI = (Integer) this.msgIdHash.get(msgIdI);
            }
            if (found) {
                this.msgIdHash.remove(msgIdI);
            }
        }
        return removed;
    }

    public byte[] encodeDiscoveryPacket(final byte msg_type, final int rId, final int errstat, final int errind, final Enumeration ve, final Object obj) throws IOException, EncodingException {
        String engineId = "";
        final TimeWindow tWindow = TimeWindow.getCurrent();
        if (tWindow.isSnmpEngineIdKnown(this.getSendToHostAddress(), this.hostPort)) {
            engineId = tWindow.getSnmpEngineId(this.getSendToHostAddress(), this.hostPort);
        }
        final TimeWindowNode node = new TimeWindowNode(engineId, 0, 0);
        return this.actualEncodePacket(msg_type, rId, errstat, errind, ve, node, obj);
    }

    @Override
    public byte[] encodePacket(final byte msg_type, final int rId, final int errstat, final int errind, final Enumeration ve, final Object obj) throws IOException, EncodingException {
        TimeWindowNode node = null;
        if (this.isDestroyed) {
            throw new EncodingException("Context can no longer be used, since it is already destroyed");
        }
        final TimeWindow tWindow = TimeWindow.getCurrent();
        if (this.isAuthoritative(msg_type)) {
            this.usmAgent.setSnmpContext(this);
            if (this.usmAgent.getSnmpEngineId() == null) {
                throw new EncodingException("UsmAgent " + this.usmAgent.getClass().getName() + " should provide Engine ID!");
            }
            tWindow.updateTimeWindow(this.usmAgent.getSnmpEngineId(), this.usmAgent.getSnmpEngineBoots(), this.usmAgent.getSnmpEngineTime(), this.isUseAuthentication());
            node = tWindow.getTimeLine(this.usmAgent.getSnmpEngineId());
        } else {
            if (!tWindow.isSnmpEngineIdKnown(this.getSendToHostAddress(), this.hostPort)) {
                throw new EncodingException("Engine ID of host " + this.getSendToHostAddress() + ", port " + this.hostPort + " is unknown (rId=" + rId + "). Perform discovery.");
            }
            final String engineId = tWindow.getSnmpEngineId(this.getSendToHostAddress(), this.hostPort);
            node = new TimeWindowNode(engineId, 0, 0);
            if (this.isUseAuthentication()) {
                if (!tWindow.isTimeLineKnown(engineId)) {
                    throw new EncodingException("Time Line of Engine ID of host " + this.getSendToHostAddress() + ", port " + this.hostPort + " is unknown. " + "Perform discovery.");
                }
                node = tWindow.getTimeLine(engineId);
            }
        }
        return this.actualEncodePacket(msg_type, rId, errstat, errind, ve, node, obj);
    }

    protected String checkContextSanity() {
        String ret = null;
        if (this.usePrivacy) {
            if (this.userPrivacyPassword == null) {
                ret = "userPrivacyPassword is null, but usePrivacy is true";
            } else if (this.userPrivacyPassword.length() == 0) {
                ret = "userPrivacyPassword is empty, but usePrivacy is true";
            } else if (!this.useAuthentication) {
                ret = "useAuthentication is false, but usePrivacy is true";
            }
        }
        if (this.useAuthentication) {
            if (this.userAuthenticationPassword == null) {
                ret = "userAuthenticationPassword is null, but useAuthentication is true";
            } else if (this.userAuthenticationPassword.length() == 0) {
                ret = "userAuthenticationPassword is empty, but useAuthentication is true";
            }
        }
        return ret;
    }

    protected byte[] actualEncodePacket(final byte msg_type, final int rId, final int errstat, final int errind, final Enumeration ve, final TimeWindowNode node, final Object obj) throws IOException, EncodingException {
        final AsnEncoderv3 enc = new AsnEncoderv3();
        final String msg = this.checkContextSanity();
        if (msg != null) {
            throw new EncodingException(msg);
        }
        final int msgId = (int) obj;
        if (AsnObject.debug > 6) {
            System.out.println(this.getClass().getName() + ".actualEncodePacket(): msgId=" + msgId + ", Pdu reqId=" + rId);
        }
        final byte[] packet = enc.EncodeSNMPv3(this, msgId, node, msg_type, rId, errstat, errind, ve);
        return packet;
    }

    @Override
    protected void processIncomingResponse(ByteArrayInputStream in) throws DecodingException, IOException {
        final AsnDecoderv3 rpdu = new AsnDecoderv3();
        byte[] bu = null;
        final int nb = in.available();
        bu = new byte[nb];
        in.read(bu);
        in = new ByteArrayInputStream(bu);
        final AsnSequence asnTopSeq = rpdu.DecodeSNMPv3(in);
        final int msgId = rpdu.getMsgId(asnTopSeq);
        Integer rid = (Integer) this.msgIdHash.get(new Integer(msgId));
        if (rid != null) {
            if (AsnObject.debug > 6) {
                System.out.println(this.getClass().getName() + ".processIncomingResponse(): msgId=" + msgId + ", Pdu reqId=" + rid);
            }
            Pdu pdu = this.getPdu(rid);
            try {
                final AsnPduSequence pduSeq = rpdu.processSNMPv3(this, asnTopSeq, bu, false);
                if (pduSeq != null) {
                    final Integer rid2 = new Integer(pduSeq.getReqId());
                    if (AsnObject.debug > 6) {
                        System.out.println(this.getClass().getName() + ".processIncomingResponse():" + " rid2=" + rid2);
                    }
                    Pdu newPdu = null;
                    if (rid2 != (int) rid) {
                        newPdu = this.getPdu(rid2);
                        if (AsnObject.debug > 3) {
                            System.out.println(this.getClass().getName() + ".processIncomingResponse(): " + "pduReqId of msgId (" + (int) rid + ") != pduReqId of Pdu (" + (int) rid2 + ")");
                        }
                        if (newPdu == null && AsnObject.debug > 3) {
                            System.out.println(this.getClass().getName() + ".processIncomingResponse(): " + "Using pduReqId of msgId (" + (int) rid + ")");
                        }
                    }
                    if (newPdu != null) {
                        pdu = newPdu;
                    }
                } else if (AsnObject.debug > 6) {
                    System.out.println(this.getClass().getName() + ".processIncomingResponse():" + " pduSeq is null.");
                }
                if (pdu != null) {
                    pdu.fillin(pduSeq);
                } else if (AsnObject.debug > 6) {
                    System.out.println(this.getClass().getName() + ".processIncomingResponse(): No Pdu with reqid " + (int) rid);
                }
            } catch (DecodingException exc) {
                if (pdu == null) {
                    throw exc;
                }
                pdu.setErrorStatus(19, exc);
                pdu.fillin(null);
            }
        } else {
            if (AsnObject.debug > 3) {
                System.out.println(this.getClass().getName() + ".processIncomingResponse(): Pdu of msgId " + msgId + " is already answered");
            }
            rid = new Integer(-1);
        }
    }

    protected boolean isAuthoritative(final byte msg_type) {
        return msg_type == -94 || msg_type == -89 || msg_type == -88;
    }

    void discoverIfNeeded(final Pdu pdu) throws IOException, PduException {
        UsmDiscoveryBean discBean = null;
        boolean isNeeded = false;
        final TimeWindow tWindow = TimeWindow.getCurrent();
        String engineId = tWindow.getSnmpEngineId(this.getSendToHostAddress(), this.hostPort);
        if (engineId == null) {
            isNeeded = true;
            discBean = new UsmDiscoveryBean(this.getSendToHostAddress(), this.hostPort, this.bindAddr, this.typeSocket);
            discBean.setRetryIntervals(pdu.getRetryIntervals());
        }
        if (this.isUseAuthentication()) {
            if (isNeeded) {
                discBean.setAuthenticationDetails(this.userName, this.userAuthenticationPassword, this.authenticationProtocol);
            } else if (!tWindow.isTimeLineKnown(engineId)) {
                isNeeded = true;
                discBean = new UsmDiscoveryBean(this.getSendToHostAddress(), this.hostPort, this.bindAddr, this.typeSocket);
                discBean.setAuthenticationDetails(this.userName, this.userAuthenticationPassword, this.authenticationProtocol);
                discBean.setRetryIntervals(pdu.getRetryIntervals());
            }
            if (isNeeded && this.isUsePrivacy()) {
                discBean.setPrivacyDetails(this.userPrivacyPassword, this.privacyProtocol);
            }
        }
        if (isNeeded) {
            discBean.startDiscovery();
        }
        if (this.contextEngineId == null || this.contextEngineId.length == 0) {
            engineId = tWindow.getSnmpEngineId(this.getSendToHostAddress(), this.hostPort);
            this.setContextEngineId(SnmpUtilities.toBytes(engineId));
        }
    }

    @Override
    public void addRequestPduListener(final RequestPduListener l, final ListeningContextPool lcontext) throws IOException {
        super.addRequestPduListener(l, lcontext);
        this.usmAgent.setSnmpContext(this);
        final TimeWindow tWindow = TimeWindow.getCurrent();
        if (this.usmAgent.getSnmpEngineId() == null) {
            throw new IOException("UsmAgent " + this.usmAgent.getClass().getName() + " should provide Engine ID!");
        }
        final TimeWindow timeWindow = tWindow;
        final UsmAgent usmAgent = this.usmAgent;
        timeWindow.setSnmpEngineId("_myusmagent", this.hostPort, this.usmAgent.getSnmpEngineId());
        tWindow.updateTimeWindow(this.usmAgent.getSnmpEngineId(), this.usmAgent.getSnmpEngineBoots(), this.usmAgent.getSnmpEngineTime(), this.isUseAuthentication());
    }

    public Object cloneParameters(final SnmpContextv3Face clContext) {
        clContext.setUserName(new String(this.userName));
        clContext.setUseAuthentication(this.useAuthentication);
        if (this.userAuthenticationPassword != null) {
            clContext.setUserAuthenticationPassword(new String(this.userAuthenticationPassword));
        }
        clContext.setAuthenticationProtocol(this.authenticationProtocol);
        clContext.setUsePrivacy(this.usePrivacy);
        if (this.userPrivacyPassword != null) {
            clContext.setUserPrivacyPassword(new String(this.userPrivacyPassword));
        }
        clContext.setPrivacyProtocol(this.privacyProtocol);
        clContext.setContextName(new String(this.contextName));
        final int l = this.contextEngineId.length;
        final byte[] newContextEngineId = new byte[l];
        System.arraycopy(this.contextEngineId, 0, newContextEngineId, 0, l);
        clContext.setContextEngineId(newContextEngineId);
        clContext.setUsmAgent(this.usmAgent);
        return clContext;
    }

    @Override
    public String getHashKey() {
        final StringBuffer buffer = new StringBuffer();
        buffer.append(this.hostname);
        buffer.append("_").append(this.hostPort);
        buffer.append("_").append(this.bindAddr);
        buffer.append("_").append(this.typeSocket);
        buffer.append("_").append(this.useAuthentication);
        buffer.append("_").append(ProtocolNames[this.authenticationProtocol]);
        buffer.append("_").append(ProtocolNames[this.privacyProtocol]);
        buffer.append("_").append(this.userAuthenticationPassword);
        buffer.append("_").append(this.userName);
        buffer.append("_").append(this.usePrivacy);
        buffer.append("_").append(this.userPrivacyPassword);
        buffer.append("_").append(SnmpUtilities.toHexString(this.contextEngineId));
        buffer.append("_").append(this.contextName);
        buffer.append("_v").append(this.getVersion());
        return buffer.toString();
    }

    @Override
    public String toString() {
        final StringBuffer buffer = new StringBuffer(this.getClass().getName() + "[");
        buffer.append("host=").append(this.hostname);
        buffer.append(", sendToHost=").append(this.getSendToHostAddress());
        buffer.append(", port=").append(this.hostPort);
        buffer.append(", bindAddress=").append(this.bindAddr);
        buffer.append(", socketType=").append(this.typeSocket);
        buffer.append(", contextEngineId=").append(SnmpUtilities.toHexString(this.contextEngineId));
        buffer.append(", contextName=").append(this.contextName);
        buffer.append(", userName=").append(this.userName);
        buffer.append(", useAuthentication=").append(this.useAuthentication);
        buffer.append(", authenticationProtocol=").append(ProtocolNames[this.authenticationProtocol]);
        buffer.append(", userAuthenticationPassword=").append(this.userAuthenticationPassword);
        buffer.append(", usePrivacy=").append(this.usePrivacy);
        buffer.append(", privacyProtocol=").append(ProtocolNames[this.privacyProtocol]);
        buffer.append(", userPrivacyPassword=").append(this.userPrivacyPassword);
        buffer.append(", #trapListeners=").append(this.trapSupport.getListenerCount());
        buffer.append(", #pduListeners=").append(this.pduSupport.getListenerCount());
        buffer.append("]");
        return buffer.toString();
    }

    static {
        SnmpContextv3Basis.next_id = 1;
    }
}
